package com.jxpanda.infrastructure.spring.toolkit;

import com.fasterxml.jackson.core.type.TypeReference;
import com.jxpanda.infrastructure.core.tollkit.json.JsonKit;
import lombok.experimental.UtilityClass;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.MediaType;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * Webflux的工具类
 */
@UtilityClass
public final class WebfluxKit {


    /**
     * 从 ServerWebExchange 中读取请求体并反序列化为指定类型的对象
     * 使用泛型方法允许调用者指定返回对象的类型
     *
     * @param exchange 交换对象，包含请求和响应的所有信息
     * @param clazz    目标类，用于反序列化请求体
     * @param <T>      泛型参数，表示返回对象的类型
     * @return 返回一个 Mono，包含反序列化后的对象
     */
    public static <T> Mono<T> readBody(ServerWebExchange exchange, Class<T> clazz) {
        return readBodyAsBytes(exchange)
                .map(bytes -> JsonKit.fromJson(bytes, clazz));
    }

    /**
     * 从 ServerWebExchange 中读取请求体并反序列化为指定类型的对象
     * 使用 TypeReference 允许更复杂的类型反序列化（例如集合的泛型类型）
     *
     * @param exchange      交换对象，包含请求和响应的所有信息
     * @param typeReference 类型引用，用于反序列化请求体
     * @param <T>           泛型参数，表示返回对象的类型
     * @return 返回一个 Mono，包含反序列化后的对象
     */
    public static <T> Mono<T> readBody(ServerWebExchange exchange, TypeReference<T> typeReference) {
        return readBodyAsBytes(exchange)
                .map(bytes -> JsonKit.fromJson(bytes, typeReference));
    }

    /**
     * 从 ServerWebExchange 中读取请求体并以字符串形式返回
     *
     * @param exchange 交换对象，包含请求和响应的所有信息
     * @return 返回一个 Mono，包含请求体的字符串表示
     */
    public static Mono<String> readBodyAsString(ServerWebExchange exchange) {
        return readBodyAsBytes(exchange)
                .map(String::new);
    }

    /**
     * 从 ServerWebExchange 中读取请求体并以字节数组形式返回
     * 该方法首先检查内容类型是否为 APPLICATION_JSON，否则抛出异常
     * 然后将请求体的数据缓冲区转换为字节数组
     *
     * @param exchange 交换对象，包含请求和响应的所有信息
     * @return 返回一个 Mono，包含请求体的字节数组表示
     */
    public static Mono<byte[]> readBodyAsBytes(ServerWebExchange exchange) {
        MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
        if (mediaType == null || !mediaType.isCompatibleWith(MediaType.APPLICATION_JSON)) {
            return Mono.error(new IllegalArgumentException("Unsupported media type: " + mediaType));
        }

        return DataBufferUtils.join(exchange.getRequest().getBody())
                .flatMap(dataBuffer -> {
                    byte[] bytes = new byte[dataBuffer.readableByteCount()];
                    dataBuffer.read(bytes);
                    // 释放 DataBuffer
                    DataBufferUtils.release(dataBuffer);
                    return Mono.just(bytes);
                });
    }


}
